package com.cango.student.algorithm;

/**
 * 滑动窗口
 *
 * 借助一个辅助队列，从头遍历数组，根据如下规则进行入队列或出队列操作：
 * 0. 如果队列为空，则当前数字下标入队列
 * 1. 如果当前数字大于队列尾，则删除队列尾，直到当前数字小于等于队列尾，或者队列空，然后当前数字入队列
 * 2. 如果当前数字小于队列尾，则当前数字下标入队列
 * 3. 如果队列头超出滑动窗口范围，则删除队列头
 */
import java.util.ArrayList;
import java.util.LinkedList;

public class Solution {
    /**
     *
     * @param num 数组
     * @param size 滑动窗口大小
     * @return
     */
    public ArrayList<Integer> maxInWindows(int[] num, int size) {
        ArrayList<Integer> result = new ArrayList<>();

        if(num == null || num.length == 0 || size == 0 || size > num.length) {
            return result;
        }

        LinkedList<Integer> queue = new LinkedList<>();

        for(int i = 0; i < num.length; i++) {
            if(!queue.isEmpty()){
                // 如果队列头元素不在滑动窗口中了，就删除头元素
                // peek 获取第一个元素，但是不移除
                if(i >= queue.peek() + size) {
                    // 出栈
                    // pop相当removeFirst
                    queue.poll();
//                    queue.pop();
                }

                // 如果当前数字大于队列尾，则删除队列尾，直到当前数字小于等于队列尾，或者队列空
                while(!queue.isEmpty() && num[i] >= num[queue.getLast()]) {
                    queue.removeLast();
                }
            }

            // 下标入队列
            queue.offer(i);

            // 滑动窗口经过三个元素，获取当前的最大值，也就是队列的头元素
            if(i + 1 >= size) {
                // 返回队列头部的元素
                result.add(num[queue.peek()]);
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        ArrayList<Integer> result = solution.maxInWindows(new int[] {2,3,4,2,6,2,5,1}, 3);
        result.forEach(System.out::println);

    }
}